/**
 * @Copyright ®2015 Sinosoft Co. Ltd. All rights reserved.<br/>
 * 项目名称 : 中科软支付平台
 * 创建日期 : 2016年12月22日
 * 修改历史 : 
 *     1. [2016年12月22日]创建文件 by liuyujia
 */
package com.sinosoft.sinopay.web.agentpay.biz.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sinosoft.sinocloud.common.biz.service.ReportLogService;
import com.sinosoft.sinocloud.platform.base.SinoBaseService;
import com.sinosoft.sinocloud.platform.common.exception.biz.IllegalRequestException;
import com.sinosoft.sinocloud.platform.common.exception.checked.RepeatRequestException;
import com.sinosoft.sinocloud.platform.common.util.DateUtil;
import com.sinosoft.sinocloud.platform.common.util.PersonIDUtil;
import com.sinosoft.sinopay.api.common.dto.pay.PaymentInfoDetailDTO;
import com.sinosoft.sinopay.api.common.dto.query.PayInfoQryParam;
import com.sinosoft.sinopay.customer.facade.CustomerFacadeService;
import com.sinosoft.sinopay.merchants.facade.MerchantInfoFacadeService;
import com.sinosoft.sinopay.payrules.config.ENUM_IP_BIZTYPE;
import com.sinosoft.sinopay.payrules.config.ENUM_IP_SECURITYLIST;
import com.sinosoft.sinopay.payrules.facade.SecurityListFacadeService;
import com.sinosoft.sinopay.paytrade.config.ENUM_PAY_STATUS;
import com.sinosoft.sinopay.paytrade.facade.PaymentTradeQueryFacadeService;
import com.sinosoft.sinopay.web.agentpay.biz.service.AgentSecurityService;
import com.sinosoft.sinopay.web.agentpay.dto.AgentEntryRequsetDTO;
import com.sinosoft.sinopay.web.agentpay.dto.TongPageReturnNotifyDTO;
import com.sinosoft.sinopay.web.util.AgentSignUtil;
import com.sinosoft.sinopay.web.util.Base64;
import com.sinosoft.sinopay.web.util.FilePathUtil;

/**  
 * 【类或接口功能描述】
 * @author liuyujia
 * @date 2016年12月22日 下午2:46:09 
 * @version V1.0  
 */
@Service
public class AgentSecurityServiceImpl extends SinoBaseService implements AgentSecurityService{
	 @Autowired
	 private MerchantInfoFacadeService merchantSecurityFacadeService;
	 @Autowired
	 private SecurityListFacadeService securityListFacadeService;
	 @Autowired
	 private PaymentTradeQueryFacadeService paymentTradeQueryFacadeService;
	 @Autowired
	 private CustomerFacadeService customerFacadeService;
	 @Autowired
     private ReportLogService reportLogService;
	 /** 商户签名信息缓存map */
	 private Map<String, String> signKeysMap;
	 
	@Override
	public void checkMerchantSignValid(String merchantCode, AgentEntryRequsetDTO tAgentEntryRequsetDTO, String signMsg) throws IllegalRequestException {
		if(signKeysMap==null || signKeysMap.size()==0){
			 //获取全部的商户签名信息
			 signKeysMap = merchantSecurityFacadeService.getAllMerchantSignKeys();
		 }
		//获取商户对应的key值
		if(signKeysMap.get(merchantCode)==null){
			throw new IllegalRequestException("商户不存在");
		}
		String tSignKey = signKeysMap.get(merchantCode);
		//封装签名参数
		 Map<String,String> tSignInfoMap = new HashMap<String,String>();
		 tSignInfoMap.put("merchantCode",merchantCode);
        tSignInfoMap.put("bizNo", tAgentEntryRequsetDTO.getBizNo());
        tSignInfoMap.put("bizType", tAgentEntryRequsetDTO.getBizType());
        tSignInfoMap.put("transType", tAgentEntryRequsetDTO.getTransType());
        tSignInfoMap.put("transDesc", tAgentEntryRequsetDTO.getTransDesc());
        tSignInfoMap.put("serialNo", tAgentEntryRequsetDTO.getSerialNo());
        tSignInfoMap.put("accountName", tAgentEntryRequsetDTO.getAccountName());
        tSignInfoMap.put("accountGender", tAgentEntryRequsetDTO.getAccountGender());
        Date tAccountBirthday = tAgentEntryRequsetDTO.getAccountBirthday();
        String birthday = DateUtil.getDateStr(tAccountBirthday, "yyyy-MM-dd");
        tSignInfoMap.put("accountBirthday", birthday);
        tSignInfoMap.put("idType", tAgentEntryRequsetDTO.getIdType());
        tSignInfoMap.put("idNo", tAgentEntryRequsetDTO.getIdNo());
        if(tAgentEntryRequsetDTO.getPayAmount()==null || tAgentEntryRequsetDTO.getPayAmount().equals(0)){
       	 throw new IllegalRequestException("金额不能为空");
        }
        String tPayAmount = transAmount(tAgentEntryRequsetDTO.getPayAmount());
        tSignInfoMap.put("payAmount",tPayAmount);//金额转换成分
        Date tRequestDate = tAgentEntryRequsetDTO.getRequestDate();
        String date = DateUtil.getDateStr(tRequestDate, "yyyy-MM-dd");
        tSignInfoMap.put("requestDate",date);
        tSignInfoMap.put("remark",tAgentEntryRequsetDTO.getRemark());
        String tCompareSignMsg = AgentSignUtil.encryMD5(tSignInfoMap,tSignKey);
        if(signMsg ==null || !signMsg.equals(tCompareSignMsg)){
            throw new IllegalRequestException("签名不合法");
        }
	}

	/**
	 * 金额元转分
	 * @param tPayAmount
	 * @return
	 */
	public String transAmount(BigDecimal tPayAmount){
		BigDecimal fen = tPayAmount.multiply(new BigDecimal("100"));
        long fenAmount = fen.intValue();
        String money = String.valueOf(fenAmount);
		return money;
		
	}

	@Override
	public void checkRequestIpValid(String tRequestIp) throws IllegalRequestException {
		//TODO
		securityListFacadeService.checkIpValid(tRequestIp, ENUM_IP_BIZTYPE.GATEWAY, ENUM_IP_SECURITYLIST.BLACK);
	}

	@Override
	public void checkRepeatRequest(String merchantCode, String serialNo) throws RepeatRequestException {
		if(serialNo==null || serialNo==""){
			throw new RepeatRequestException("交易流水号不能为空");
		}
		reportLogService.checkRepeatRequest(merchantCode,serialNo);
	}
	
	@Override
	public void checkPayStatus(String payNo) throws IllegalRequestException {
		PayInfoQryParam tPayInfoQryParam = new PayInfoQryParam();
		tPayInfoQryParam.setPayNo(payNo);
		List<PaymentInfoDetailDTO> tPaymentInfoDetailDTO = paymentTradeQueryFacadeService.queryPaymentDetailsByParam(tPayInfoQryParam);
		if(tPaymentInfoDetailDTO.get(0)==null || tPaymentInfoDetailDTO.size()<0){
			throw new IllegalRequestException("支付订单号有误");
		}
		if(tPaymentInfoDetailDTO.get(0).getPayState().equalsIgnoreCase(ENUM_PAY_STATUS.PAY_PAYED.code())){
			throw new IllegalRequestException("支付订单号不可重复支付");
		}
	}
	
	@Override
	public void checkPayNoSignValid(String PayNo, String PayWay, String SignMsg) throws IllegalRequestException {
		PayInfoQryParam tPayInfoQryParam = new PayInfoQryParam();
		tPayInfoQryParam.setPayNo(PayNo);
		tPayInfoQryParam.setPayType(PayWay);
		List<PaymentInfoDetailDTO> tPaymentInfoDetailDTO = paymentTradeQueryFacadeService.queryPaymentDetailsByParam(tPayInfoQryParam);
		if(tPaymentInfoDetailDTO.get(0)==null || tPaymentInfoDetailDTO.size()<0){
			throw new IllegalRequestException("支付订单号有误");
		}
		//封装签名参数
		Map<String,String> tSignInfoMap = new HashMap<String,String>();
		tSignInfoMap.put("merchantCode",tPaymentInfoDetailDTO.get(0).getMerchantCode());
        tSignInfoMap.put("bizNo", tPaymentInfoDetailDTO.get(0).getBizNo());
        tSignInfoMap.put("bizType", tPaymentInfoDetailDTO.get(0).getBizType());
        tSignInfoMap.put("transType", "");
        tSignInfoMap.put("transDesc", tPaymentInfoDetailDTO.get(0).getTradeDesc());
        tSignInfoMap.put("serialNo", tPaymentInfoDetailDTO.get(0).getSerialNo());
        tSignInfoMap.put("accountName", tPaymentInfoDetailDTO.get(0).getAccountName());
        tSignInfoMap.put("idType", tPaymentInfoDetailDTO.get(0).getIdType());
        String idNo = tPaymentInfoDetailDTO.get(0).getIdNo();
        tSignInfoMap.put("idNo", idNo);
        tSignInfoMap.put("accountGender", PersonIDUtil.getGenderByIdCard(idNo));
        tSignInfoMap.put("accountBirthday", PersonIDUtil.getBirthByIdCard(idNo));
        
        if(tPaymentInfoDetailDTO.get(0).getPayAmount()==null || tPaymentInfoDetailDTO.get(0).getPayAmount().equals(0)){
       	 	throw new IllegalRequestException("金额不能为空");
        }
        String tPayAmount = transAmount(tPaymentInfoDetailDTO.get(0).getPayAmount());
        tSignInfoMap.put("payAmount",tPayAmount);//金额转换成分
        Date tRequestDate = tPaymentInfoDetailDTO.get(0).getCreateDate();
        String date = DateUtil.getDateStr(tRequestDate, "yyyy-MM-dd");
        tSignInfoMap.put("requestDate", date);
        tSignInfoMap.put("remark",tPaymentInfoDetailDTO.get(0).getRemark());
        if(signKeysMap==null || signKeysMap.size()==0){
			 //获取全部的商户签名信息
			 signKeysMap = merchantSecurityFacadeService.getAllMerchantSignKeys();
		 }
		//获取商户对应的key值
		if(signKeysMap.get(tPaymentInfoDetailDTO.get(0).getMerchantCode())==null){
			throw new IllegalRequestException("商户不存在");
		}
		String tSignKey = signKeysMap.get(tPaymentInfoDetailDTO.get(0).getMerchantCode());
        String tCompareSignMsg = AgentSignUtil.encryMD5(tSignInfoMap,tSignKey);
        if(SignMsg ==null || !SignMsg.equals(tCompareSignMsg)){
            throw new IllegalRequestException("签名不合法");
        }
	}

	@Override
	public boolean checkReturnInfoVerify(TongPageReturnNotifyDTO tongPageReturnNotifyDTO) throws IllegalRequestException {
		logger.info("校验回调信息合法性开始");
		String sign = "";
        if (tongPageReturnNotifyDTO.getSign() != null) {
            sign = tongPageReturnNotifyDTO.getSign();
        }
        boolean isSign = getSignVeryfy(tongPageReturnNotifyDTO, sign);

        //写日志记录（若要调试，请取消下面两行注释）
        //String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数：" + AlipayCore.createLinkString(params);
        //AlipayCore.logResult(sWord);

        if (isSign) {
            return true;
        } else {
            return false;
        }
	}
	
	/**
	 * 根据反馈回来的信息，生成签名结果
	 * @param tongPageReturnNotifyDTO
	 * @param sign
	 * @return
	 */
	private static String encoding = "utf-8";
	private boolean getSignVeryfy(TongPageReturnNotifyDTO tongPageReturnNotifyDTO, String sign) {
		logger.info("生成签名结果开始");
		Map<String, String> map = new HashMap<String, String>();
		map.put("resp_code", tongPageReturnNotifyDTO.getResp_code());
		map.put("resp_msg", tongPageReturnNotifyDTO.getResp_msg());
		map.put("sign_type", tongPageReturnNotifyDTO.getSign_type());
		map.put("sign", tongPageReturnNotifyDTO.getSign());
		map.put("mer_id", tongPageReturnNotifyDTO.getMer_id());
		map.put("order_no", tongPageReturnNotifyDTO.getOrder_no());
		map.put("serial_no", tongPageReturnNotifyDTO.getSerial_no());
		map.put("orde_time", tongPageReturnNotifyDTO.getOrde_time());
		map.put("status", tongPageReturnNotifyDTO.getStatus());
		map.put("succ_time", tongPageReturnNotifyDTO.getSucc_time());
		map.put("succ_amt", tongPageReturnNotifyDTO.getSucc_amt());
		map.put("remark", tongPageReturnNotifyDTO.getRemark());
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = paraFilter(map);
        //获取待签名字符串
        String preSignStr = createLinkString(sParaNew);
        //获得签名验证结果
        try {
        	logger.info("待验签字符串="+preSignStr);
        	logger.info("签名="+sign);
			return VerifyMsg(sign, preSignStr, "config/yixun.cer");
		} catch (Exception e) {
			e.printStackTrace();
		}
        return false;
	}

	/**
	 * 验证签名
	 * @param TobeVerified
	 * @param PlainText
	 * @param CertFile
	 * @return
	 * @throws Exception
	 */
	private boolean VerifyMsg(String TobeVerified, String PlainText, String CertFile) throws Exception{
		logger.info("待验证签名的密文:"+TobeVerified);
		logger.info("待验证签名的明文:"+PlainText);
		logger.info("签名者公钥证书:"+CertFile);
		InputStream certfile = new FileInputStream(new File(FilePathUtil.getClassBuildPath() + CertFile));
		CertificateFactory cf = CertificateFactory.getInstance("X.509");
		X509Certificate x509cert = null;
		try {
			x509cert = (X509Certificate) cf.generateCertificate(certfile);
		} catch (Exception ex) {
			if (certfile != null)
				certfile.close();
			throw ex;
		}
		RSAPublicKey pubkey = (RSAPublicKey) x509cert.getPublicKey();
		Signature verify = Signature.getInstance("SHA256withRSA");
		verify.initVerify(pubkey);
		verify.update(PlainText.getBytes(encoding));
		if (verify.verify(Base64.decode(TobeVerified))) {
			return true;
		}
		return false;
	}

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * @param sParaNew
	 * @return
	 */
	private String createLinkString(Map<String, String> params) {
		logger.info("拼接签名字符串");
		 List<String> keys = new ArrayList<String>(params.keySet());
	        Collections.sort(keys);
	        String prestr = "";
	        for (int i = 0; i < keys.size(); i++) {
	            String key = keys.get(i);
	            String value = params.get(key);

	            if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
	                prestr = prestr + key + "=" + value;
	            } else {
	                prestr = prestr + key + "=" + value + "&";
	            }
	        }
	        return prestr;
	}

	/**
	 * 除去数组中的空值和签名参数
	 * @param map
	 * @return
	 */
	private Map<String, String> paraFilter(Map<String, String> sArray) {
		logger.info("过滤验签参数空值");
		 Map<String, String> result = new HashMap<String, String>();
	        if (sArray == null || sArray.size() <= 0) {
	            return result;
	        }
	        for (String key : sArray.keySet()) {
	            String value = sArray.get(key);
	            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
	                || key.equalsIgnoreCase("sign_type")) {
	                continue;
	            }
	            result.put(key, value);
	        }
	        return result;
	}
}
